/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  							 script_expression_type.cpp

	$Header: /heroes4/script_expression_type.cpp $

	$NoKeywords: $

 ************************************************************************/

#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "script_expression_type.h"

#include <bitset>
#include <cassert>
#include <cstdlib>
#include <map>
#include <utility>

#include "elements_of.h"
#include "enum_operations.h"
#include "script_context.h"
#include "string_insensitive_compare.h"

// --------------------------------------------------------------------------
// Unnamed namespace
// --------------------------------------------------------------------------

namespace
{
	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	typedef std::bitset< k_script_context_count > t_script_context_mask;

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	t_script_context_mask build_script_context_mask(
		t_script_context const *	contexts,
		size_t						count )
	{
		t_script_context_mask result;

		while ( count > 0 )
		{
			t_script_context context = *contexts++;
			assert( context >= 0 && context < k_script_context_count );
			assert( !result.test( context ) );

			result.set( context );

			--count;
		}

		return result;
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	typedef std::string t_numeric_keyword_table[ k_script_numeric_expression_type_count ];
	t_numeric_keyword_table const & get_numeric_keyword_table()
	{
		static std::string const k_keyword_table[ k_script_numeric_expression_type_count ] =
		{
			"creatures",
			"day",
			"dow",	// day of week
			"/",
			"exp_level",
			"mastery",
			"lit",
			"materials",
			"-",
			"month",
			"neg",
			"player",
			"+",
			"rand",
			"%",
			"*",
			"total_creatures",
			"total_heroes",
			"var",
			"week",
			"wom",	// week of month
		};

		return k_keyword_table;
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	typedef std::map< std::string, t_script_numeric_expression_type, t_string_insensitive_less > t_numeric_type_map;
	t_numeric_type_map const & get_numeric_type_map()
	{
		static t_numeric_type_map type_map;
		static bool initialized = false;
		if ( !initialized )
		{
			t_numeric_keyword_table const & k_keyword_table = get_numeric_keyword_table();

			typedef t_script_numeric_expression_type t_type;
			t_type type;
			for (	type = t_type( 0 );
					type < k_script_numeric_expression_type_count;
					enum_incr( type ) )
				type_map.insert( std::make_pair( k_keyword_table[ type ], type ) );

			initialized = true;
		}

		return type_map;
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	typedef std::string t_boolean_keyword_table[ k_script_boolean_expression_type_count ];
	t_boolean_keyword_table const & get_boolean_keyword_table()
	{
		static std::string const k_keyword_table[ k_script_boolean_expression_type_count ] =
		{
			"and",
			"has_alignment",
			"has_hero",
			"give_skill?",
			"==",
			"false",
			">",
			">=",
			"has_artifact",
			"is_dead",
			"is_imprisoned",
			"<",
			"<=",
			"not",
			"or",
			"is_alignment",
			"is_color",
			"is_computer",
			"is_eliminated",
			"is_human",
			"owns_hero",
			"owns_town",
			"true",
			"var",
		};

		return k_keyword_table;
	}

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	typedef std::map< std::string, t_script_boolean_expression_type, t_string_insensitive_less > t_boolean_type_map;
	t_boolean_type_map const & get_boolean_type_map()
	{
		static t_boolean_type_map type_map;
		static bool initialized = false;
		if ( !initialized )
		{
			t_boolean_keyword_table const & k_keyword_table = get_boolean_keyword_table();

			typedef t_script_boolean_expression_type t_type;
			t_type type;
			for (	type = t_type( 0 );
					type < k_script_boolean_expression_type_count;
					enum_incr( type ) )
				type_map.insert( std::make_pair( k_keyword_table[ type ], type ) );

			initialized = true;
		}

		return type_map;
	}

} // Unnamed namespace

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
std::string const & get_keyword(
	t_script_numeric_expression_type expression_type )
{
	assert( expression_type >= 0 && expression_type < k_script_numeric_expression_type_count );

	static t_numeric_keyword_table const & k_keyword_table = get_numeric_keyword_table();
	return k_keyword_table[ expression_type ];
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
t_script_numeric_expression_type get_script_numeric_expression_type(
	std::string const &	keyword )
{
	static t_numeric_type_map const & k_type_map = get_numeric_type_map();

	t_numeric_type_map::const_iterator map_iter = k_type_map.find( keyword );
	if ( map_iter == k_type_map.end() )
		return k_script_numeric_none;

	return map_iter->second;
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
bool available(
	t_script_numeric_expression_type	expression_type,
	t_script_context					context )
{
	assert( expression_type >= 0 && expression_type < k_script_numeric_expression_type_count );

	switch ( expression_type )
	{
	case k_script_numeric_creatures_in_army:
	case k_script_numeric_hero_experience_level:
	case k_script_numeric_hero_skill_mastery:
	case k_script_numeric_total_creatures_in_army:
	case k_script_numeric_total_heroes_in_army:
		{
			static t_script_context const k_contexts[] =
			{
				k_script_context_hero,
				k_script_context_hero_in_combat,
				k_script_context_town,
				k_script_context_town_in_combat,
				k_script_context_town_with_visitor,
				k_script_context_placed_event,
				k_script_context_seers_hut,
				k_script_context_quest_guard,
				k_script_context_quest_gate,
				k_script_context_army,
				k_script_context_army_in_combat,
				k_script_context_garrison,
				k_script_context_garrison_in_combat,
				k_script_context_garrison_with_visitor,
			};

			static t_script_context_mask const k_context_mask =
				build_script_context_mask( k_contexts, ELEMENTS_OF( k_contexts ) );

			return k_context_mask[ context ];
		}
	}

	return true;
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
std::string const & get_keyword(
	t_script_boolean_expression_type expression_type )
{
	assert( expression_type >= 0 && expression_type < k_script_boolean_expression_type_count );

	static t_boolean_keyword_table const & k_keyword_table = get_boolean_keyword_table();
	return k_keyword_table[ expression_type ];
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
t_script_boolean_expression_type get_script_boolean_expression_type(
	std::string const &	keyword )
{
	static t_boolean_type_map const & k_type_map = get_boolean_type_map();

	t_boolean_type_map::const_iterator map_iter = k_type_map.find( keyword );
	if ( map_iter == k_type_map.end() )
		return k_script_boolean_none;

	return map_iter->second;
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
bool available(
	t_script_boolean_expression_type	expression_type,
	t_script_context					context )
{
	assert( expression_type >= 0 && expression_type < k_script_boolean_expression_type_count );

	switch ( expression_type )
	{
	case k_script_boolean_army_has_alignment:
	case k_script_boolean_army_has_hero:
	case k_script_boolean_can_give_skill:
		{
			static t_script_context const k_contexts[] =
			{
				k_script_context_hero,
				k_script_context_hero_in_combat,
				k_script_context_town,
				k_script_context_town_in_combat,
				k_script_context_town_with_visitor,
				k_script_context_placed_event,
				k_script_context_seers_hut,
				k_script_context_quest_guard,
				k_script_context_quest_gate,
				k_script_context_army,
				k_script_context_army_in_combat,
				k_script_context_garrison,
				k_script_context_garrison_in_combat,
				k_script_context_garrison_with_visitor,
			};

			static t_script_context_mask const k_context_mask =
				build_script_context_mask( k_contexts, ELEMENTS_OF( k_contexts ) );

			return k_context_mask[ context ];
		}
	}

	return true;
}
